﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

using ApproveFlowEngine.Core.Base;
using ApproveFlowEngine.Core.Nodes;
using ApproveFlowEngine.Enums.Core;

using Volo.Abp;
using Volo.Abp.Domain.Entities;

namespace ApproveFlowEngine.Core
{
    public class WorkFlow : Entity<Guid>
    {
        #region Public Constructors

        public WorkFlow(Guid id, long flowNo, string flowName, string remark = "", ApproveType approveType = ApproveType.General, ApproveFlowStatus flowStatus = ApproveFlowStatus.None)
        {
            Id = id;
            FlowNo = flowNo;
            FlowName = flowName;
            Remark = remark;
            ApproveType = approveType;
            FlowStatus = flowStatus;
            
        }

        #endregion Public Constructors

        

        #region Private Constructors

        //private WorkFlow()
        //{
        //}

        #endregion Private Constructors



        #region Public Properties

        public ApproveType ApproveType { get; set; }

        [MaxLength(255)]
        public string FlowName { get; set; }

        public long FlowNo { get; set; }
        public ApproveFlowStatus FlowStatus { get; protected set; }

        public bool IsCompleted { get; set; }

        public List<WorkNode> Nodes { get; set; } = new List<WorkNode>();

        [MaxLength(1023)]
        public string? Remark { get; set; }

        [NotMapped]
        public WorkNodeSerial? Root => BuildTree();

        #endregion Public Properties



        #region Public Methods

        public WorkFlow AddNode(WorkNode newNode)
        {
            if (FlowStatus > ApproveFlowStatus.Draft)
            {
                throw new BusinessException("approve flow is already starting,can not edit！");
            }
            if (Nodes.Any(c => c.Id == newNode.Id))
            {
                throw new BusinessException($"node id [{newNode.Id}] is already existed!");
            }
            if (newNode.Parent is null)
            {
                Root.AddChild(newNode);
            }
            else
            {
                newNode.Parent.AddChild(newNode);
            }
            return this;
        }

        public WorkNodeSerial? BuildTree()
        {
            var root = Nodes.First(c => c.ParentId is null);
            if (root is WorkNodeSerial rootSerial)
            {
                BuildTree(root,Nodes);
                return rootSerial;
            }
            return null;
        }

        public bool ExecApprove(Guid leafId, ApproveNodeStatus status)
        {
            if (Root is null)
            {
                throw new Exception("未添加审批节点！");
            }

            if (IsCompleted)
            {
                Console.WriteLine("审批已结束");
                return false;
            }

            var isApprove = Root.ExecApprove(leafId, status);
            if (Root.Status is ApproveNodeStatus.Reject)
            {
                FlowStatus = ApproveFlowStatus.Reject;
                IsCompleted = true;
                return false;
            }
            if (Root.Status is ApproveNodeStatus.Pass)
            {
                FlowStatus = ApproveFlowStatus.Pass;
                IsCompleted = true;

                return true;
            }
            return false;
        }

        public void ReSetNode()
        {
            Nodes = Nodes.Where(c=>c.ParentId is null)
                .ToList();
        }

        public void SetRoot(WorkNodeSerial nodeSerial)
        {
            Nodes.Clear();
            Nodes.Add(nodeSerial);
            Nodes.AddRange(nodeSerial.GetAllNodes());
        }
        public void Start()
        {
            
                if (Root is null)
                {
                    throw new Exception("未添加审批节点！");
                }

                if (IsCompleted)
                {
                    throw new Exception("审批已结束");
                }
            Root.Init();
            FlowStatus = ApproveFlowStatus.Activity;
        }

        #endregion Public Methods



        #region Private Methods

        private void BuildTree(WorkNode parent, IEnumerable<WorkNode> nodes)
        {
            if (parent is WorkNodeComplex parentComplex)
            {
                var children = nodes.Where(c => c.ParentId == parent.Id).OrderBy(c => c.Index);
                foreach (var child in children)
                {
                    BuildTree(child, nodes);
                    parentComplex.AddChild(child);
                }
            }
        }

        #endregion Private Methods
    }
}